/*   Copyright © 2009 Mark Gil Torres
 *
 *   This file is part of mtoolkit.
 *
 *   mtoolkit is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   any later version.
 *
 *   mtoolkit is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with mtoolkit.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef REAL_H
#define REAL_H

#include "integer.h"
#include "rational.h"
#include <string>

namespace mt {

  class Real {
    public:
      Real(void);
      Real(Real const& inValue);

      Real(double const& inValue);
      Real(std::string const& inValue);
      Real(Integer& inValue);
      Real(Rational& inValue);

      Real& operator=(double const& inValue);
      Real& operator=(std::string const& inValue);
      Real& operator=(Real const& inValue);

      mpf_class const getValue(void);

      friend Real const operator+(Real const& inValue1, double const& inValue2);
      friend Real const operator-(Real const& inValue1, double const& inValue2);
      friend Real const operator*(Real const& inValue1, double const& inValue2);
      friend Real const operator/(Real const& inValue1, double const& inValue2);
      friend Real const operator+(double const& inValue1, Real const& inValue2);
      friend Real const operator-(double const& inValue1, Real const& inValue2);
      friend Real const operator*(double const& inValue1, Real const& inValue2);
      friend Real const operator/(double const& inValue1, Real const& inValue2);

      friend Real const operator+(Real const& inValue1, Real const& inValue2);
      friend Real const operator-(Real const& inValue1, Real const& inValue2);
      friend Real const operator*(Real const& inValue1, Real const& inValue2);
      friend Real const operator/(Real const& inValue1, Real const& inValue2);

      friend std::ostream& operator<<(std::ostream& ostr, Real const& inValue);
      friend std::istream& operator>>(std::istream& istr, Real& inValue);

      friend bool operator<(Real const& inValue1, Real const& inValue2);
      friend bool operator<=(Real const& inValue1, Real const& inValue2);
      friend bool operator>(Real const& inValue1, Real const& inValue2);
      friend bool operator>=(Real const& inValue1, Real const& inValue2);
      friend bool operator==(Real const& inValue1, Real const& inValue2);

      friend Real convertRfromZ(Integer inValue);
      friend Real convertRfromQ(Rational inValue);
    private:
      mpf_class mValue;
  };

// constructors

  Real::Real(void) {
    mValue = 0;
  }

  Real::Real(Real const& inValue) : mValue(inValue.mValue) {}

  Real::Real(double const& inValue) {
    mValue = inValue;
  }

  Real::Real(std::string const& inValue) {
    mValue = inValue;
  }

  Real::Real(Integer& inValue) {
    mpz_class inValueTemp;
    inValueTemp = inValue.getValue();
    mValue = inValueTemp;
  }

  Real::Real(Rational& inValue) {
    Integer nTemp, dTemp;
    nTemp = inValue.getNumerator();
    dTemp = inValue.getDenominator();
    Real tempN(nTemp);
    Real tempD(dTemp);
    mValue = tempN.mValue / tempD.mValue;
  }

// assignment operator

  Real& Real::operator=(double const& inValue) {
    mValue = inValue;
    return *this;
  }

  Real& Real::operator=(std::string const& inValue) {
    mValue = inValue;
    return *this;
  }

  Real& Real::operator=(Real const& inValue) {
    mValue = inValue.mValue;
    return *this;
  }

// getter

  mpf_class const Real::getValue(void) {
    return mValue;
  }

// arithmetic operators

  Real const operator+(Real const& inValue1, double const& inValue2) {
    Real temp;
    temp.mValue = inValue1.mValue + inValue2;
    return temp;
  }

  Real const operator-(Real const& inValue1, double const& inValue2) {
    Real temp;
    temp.mValue = inValue1.mValue - inValue2;
    return temp;
  }

  Real const operator*(Real const& inValue1, double const& inValue2) {
    Real temp;
    temp.mValue = inValue1.mValue * inValue2;
    return temp;
  }

  Real const operator/(Real const& inValue1, double const& inValue2) {
    Real temp;
    temp.mValue = inValue1.mValue / inValue2;
    return temp;
  }

  Real const operator+(double const& inValue1, Real const& inValue2) {
    Real temp;
    temp.mValue = inValue1 + inValue2.mValue;
    return temp;
  }

  Real const operator-(double const& inValue1, Real const& inValue2) {
    Real temp;
    temp.mValue = inValue1 - inValue2.mValue;
    return temp;
  }

  Real const operator*(double const& inValue1, Real const& inValue2) {
    Real temp;
    temp.mValue = inValue1 * inValue2.mValue;
    return temp;
  }

  Real const operator/(double const& inValue1, Real const& inValue2) {
    Real temp;
    temp.mValue = inValue1 / inValue2.mValue;
    return temp;
  }

  Real const operator+(Real const& inValue1, Real const& inValue2) {
    Real temp;
    temp.mValue = inValue1.mValue + inValue2.mValue;
    return temp;
  }

  Real const operator-(Real const& inValue1, Real const& inValue2) {
    Real temp;
    temp.mValue = inValue1.mValue - inValue2.mValue;
    return temp;
  }

  Real const operator*(Real const& inValue1, Real const& inValue2) {
    Real temp;
    temp.mValue = inValue1.mValue * inValue2.mValue;
    return temp;
  }

  Real const operator/(Real const& inValue1, Real const& inValue2) {
    Real temp;
    temp.mValue = inValue1.mValue / inValue2.mValue;
    return temp;
  }

// iostream operators

  std::ostream& operator<<(std::ostream& ostr, Real const& inValue) {
    ostr << inValue.mValue;
    return ostr;
  }

  std::istream& operator>>(std::istream& istr, Real& inValue) {
    Real temp;
    istr >> temp.mValue;
    inValue.mValue = temp.mValue;
    return istr;
  }

// relational operators

  bool operator<(Real const& inValue1, Real const& inValue2) {
    int temp;
    mpf_class inValue1Temp(inValue1.mValue);
    mpf_class inValue2Temp(inValue2.mValue);
    temp = mpf_cmp(inValue1Temp.get_mpf_t(),inValue2Temp.get_mpf_t());
    if (temp < 0) return true;
    return false;
  }

  bool operator<=(Real const& inValue1, Real const& inValue2) {
    int temp;
    mpf_class inValue1Temp(inValue1.mValue);
    mpf_class inValue2Temp(inValue2.mValue);
    temp = mpf_cmp(inValue1Temp.get_mpf_t(),inValue2Temp.get_mpf_t());
    if (temp <= 0) return true;
    return false;
  }

  bool operator>(Real const& inValue1, Real const& inValue2) {
    int temp;
    mpf_class inValue1Temp(inValue1.mValue);
    mpf_class inValue2Temp(inValue2.mValue);
    temp = mpf_cmp(inValue1Temp.get_mpf_t(),inValue2Temp.get_mpf_t());
    if (temp > 0) return true;
    return false;
  }

  bool operator>=(Real const& inValue1, Real const& inValue2) {
    int temp;
    mpf_class inValue1Temp(inValue1.mValue);
    mpf_class inValue2Temp(inValue2.mValue);
    temp = mpf_cmp(inValue1Temp.get_mpf_t(),inValue2Temp.get_mpf_t());
    if (temp >= 0) return true;
    return false;
  }

  bool operator==(Real const& inValue1, Real const& inValue2) {
    int temp;
    mpf_class inValue1Temp(inValue1.mValue);
    mpf_class inValue2Temp(inValue2.mValue);
    temp = mpf_cmp(inValue1Temp.get_mpf_t(),inValue2Temp.get_mpf_t());
    if (temp == 0) return true;
    return false;
  }

  Real convertRfromZ(Integer inValue) {
    Real temp;
    temp.mValue = inValue.getValue();
    return temp;
  }

  Real convertRfromQ(Rational inValue) {
    Real temp(inValue);
    return temp;
  }

}

#endif
